#include "MeshAlgorithm.hpp"
#include "PolyMeshView.hpp"
#include "PolyMesh.hpp"

PolyMeshView::PolyMeshView(VertexBuffer& aVerts,
                           PolyBuffer&   aPolys)
: mVerts(aVerts), polys(aPolys)
{
   mPolyScaling = 1.0f;
   mSubdivisionLevel = 0;
   mUseMaterialList = false;
   mUseCuttingPlane = false;
}

void PolyMeshView::Reset(VertexBuffer& aVerts,
                         PolyBuffer&   aPolys)
{
   mVerts.ref(aVerts);
   polys.ref(aPolys);
   BuildMesh();
}

namespace
{
   void SortFace(Polyhedron::FaceVerts& aFace)
   {
      if (aFace[0] > aFace[2]) std::swap(aFace[0], aFace[2]);
      if (aFace[1] > aFace[2]) std::swap(aFace[1], aFace[2]);
      if (aFace[0] > aFace[1]) std::swap(aFace[0], aFace[1]);
   }
}
void PolyMeshView::GetUsedPolys(std::vector<int>& aUsedPolys)
{

   // Find all polyhedra we want to display
   for (size_t i = 0; i < polys.size(); ++i)
   {
      Polyhedron& p = polys[i];
      int level = 0;
      for (int r = p.parent; r != -1; r = polys[r].parent) 
         ++level;
      if (ShowPoly(p, level, i))
      {
         aUsedPolys.push_back(i);
      }
   }

}
void PolyMeshView::BuildScaledMesh()
{
   mVertexLinks.clear();
   mMesh.verts.clear();
   mMesh.faces.clear();

   std::vector<int> usedPolys;
   GetUsedPolys(usedPolys);

   for (size_t i = 0; i < usedPolys.size(); ++i)
   {
      int p = usedPolys[i];
      Polyhedron& poly = polys[p];
      TesselatePolyScaled(poly);
   }
}

void PolyMeshView::BuildMesh()
{
   //if (mPolyScaling < 1.0)
   {
      BuildScaledMesh();
      return;
   }
   
   //MeshAlgorithm::CheckValidMesh(*mPolyMesh);
   FaceSet shownFaces;

   mVertexLinks.clear();
   mMesh.verts.clear();
   mMesh.faces.clear();

   std::vector<int> usedPolys;
   GetUsedPolys(usedPolys);

   // Remove interior polyhedra
   //{
   //   typedef std::map<Polyhedron::FaceVerts, int> FaceMap;
   //   FaceMap faces;
   //   
   //   for (size_t i = 0; i < usedPolys.size(); ++i)
   //   {
   //      Polyhedron& p = polys[usedPolys[i]];
   //      int faceCount = p.GetFaceCount();
   //      for (int v = 0; v < faceCount; ++v)
   //      {
   //         Polyhedron::FaceVerts f;
   //         p.GetFace(v, f);
   //         SortFace(f);
   //         FaceMap::iterator iter = faces.find(f);
   //         if (iter != faces.end())
   //         {
   //            ++iter->second;
   //         }
   //         else
   //         {
   //            faces[f] = 1;
   //         }
   //      }
   //   }
   //   for (std::map<Polyhedron::FaceVerts, int>::iterator i = faces.begin(); i != faces.end(); ++i)
   //   {
   //      if (i->second == 1)
   //      {
   //         shownFaces.insert(i->first);
   //      }
   //   }

   //   std::vector<int> outsidePolys;
   //   for (size_t i = 0; i < usedPolys.size(); ++i)
   //   {
   //      Polyhedron& p = polys[usedPolys[i]];
   //      int interiorFaces = 0;
   //      int faceCount = p.GetFaceCount();
   //      for (int v = 0; v < faceCount; ++v)
   //      {
   //         Polyhedron::FaceVerts f;
   //         p.GetFace(v, f);
   //         SortFace(f);
   //         int faceUsed = faces[f];
   //         assert(faceUsed < 3);
   //         if (faceUsed > 1) ++interiorFaces;
   //      }
   //      if (interiorFaces != faceCount)
   //      {
   //         outsidePolys.push_back(usedPolys[i]);
   //      }
   //   }
   //   std::swap(outsidePolys, usedPolys);
   //}

   std::vector<int> usedVertices;

   //// Record all vertices belonging to selected polyhedra
   //for (size_t i = 0; i < usedPolys.size(); ++i)
   //{
   //   Polyhedron& p = polys[usedPolys[i]];
   //   for (int v = 0; v < p.arity; ++v)
   //   {
   //      usedVertices.push_back(p.vertex[v]);
   //   }
   //}

   //std::sort(usedVertices.begin(), usedVertices.end());
   //usedVertices.erase(std::unique(usedVertices.begin(), usedVertices.end()), usedVertices.end());
   usedVertices.resize(mVerts.size());
   for (size_t i = 0; i < mVerts.size(); ++i)
   {
      usedVertices[i]=i;
   }
   mMesh.verts.resize(usedVertices.size());
   for (size_t i = 0; i < usedVertices.size(); ++i)
   {
      int v = usedVertices[i];
      mVertexLinks.push_back(VertexLink(v, i));
      mMesh.verts[i] = mVerts[v];
   }

   for (size_t i = 0; i < usedPolys.size(); ++i)
   {
      int p = usedPolys[i];
      Polyhedron& poly = polys[p];
      TesseleatePoly(poly, shownFaces);
   }

   //MeshAlgorithm::CheckValidMesh(mMesh);
}

bool PolyMeshView::ShowPoly(Polyhedron& aPoly,
                            int         aSubdivisionLevel,
                            int         aPolyIndex)
{
   bool ok = (aSubdivisionLevel == mSubdivisionLevel);
   if (ok && mUseMaterialList)
   {
      ok = (mShownMaterials.find(aPoly.material) != mShownMaterials.end());
   }
   if (ok && !mShownPolyList.empty())
   {
      ok = std::binary_search(mShownPolyList.begin(), mShownPolyList.end(), aPolyIndex);
   }
   return ok;
}

void PolyMeshView::TesseleatePoly(Polyhedron& aPoly, FaceSet& aShownFaces)
{
   int verts[6];
   for (int i = 0; i < aPoly.arity; ++i)
   {
      VertexLinkMap::left_const_iterator iter = mVertexLinks.left.find(aPoly.vertex[i]);
      assert(iter != mVertexLinks.left.end());
      verts[i] = iter->second;
      assert(verts[i] <= aPoly.vertex[i]);
   }
   const int* faces;
   if (aPoly.arity == 4) faces = Polyhedron::sTetFaces;
   else faces = Polyhedron::sOctFaces;
   int vInd = 0;
   int faceCount = aPoly.arity == 4 ? 4 : 8;
   Face f;
   f.materials[0] = aPoly.material;
   f.materials[1] = aPoly.material;
   for (int i = 0; i < faceCount; ++i)
   {
      Polyhedron::FaceVerts polyFace;
      aPoly.GetFace(i, polyFace);
      SortFace(polyFace);
      if (aShownFaces.find(polyFace) != aShownFaces.end())
      {
         f.vertex[0] = verts[faces[vInd++]];
         f.vertex[1] = verts[faces[vInd++]];
         f.vertex[2] = verts[faces[vInd++]];
         mMesh.faces.push_back(f);
      }
      else
      {
         vInd+=3;
      }
   }
}

void PolyMeshView::TesselatePolyScaled(Polyhedron& aPoly)
{
   assert(aPoly.arity == 4 || aPoly.arity == 6);
   VertexList& verts = *mVerts;
   Vec3 center;
   for (int i = 0; i < aPoly.arity; ++i)
   {
      center += verts[aPoly.vertex[i]];
   }
   center /= (float)aPoly.arity;
   int firstVertex = mMesh.verts.size();

   for (int i = 0; i < aPoly.arity; ++i)
   {
      Vec3 v = verts[aPoly.vertex[i]];
      mMesh.verts.push_back((v - center) * mPolyScaling + center);
   }

   const int* faces;
   if (aPoly.arity == 4) faces = Polyhedron::sTetFaces;
   else faces = Polyhedron::sOctFaces;
   int vInd = 0;
   int faceCount = aPoly.arity == 4 ? 4 : 8;
   Face f;
   f.materials[0] = aPoly.material;
   f.materials[1] = aPoly.material;
   for (int i = 0; i < faceCount; ++i)
   {
      Polyhedron::FaceVerts polyFace;
      aPoly.GetFace(i, polyFace);
      SortFace(polyFace);
      f.vertex[0] = faces[vInd++] + firstVertex;
      f.vertex[1] = faces[vInd++] + firstVertex;
      f.vertex[2] = faces[vInd++] + firstVertex;
      for (int j = 0; j < 3; ++j) assert(f.vertex[j] < mMesh.verts.size());
      mMesh.faces.push_back(f);
   }
}

void PolyMeshView::SetShownVertices(const std::vector<int>& aVerts)
{
   mShownVertexList = aVerts;
   std::sort(mShownVertexList.begin(), mShownVertexList.end());
   UpdateShownVertices();
}

void PolyMeshView::RemoveShownVertices(const std::vector<int>& aVerts)
{
   if (mShownVertexList.empty())
   {
      size_t vertCount(mVerts.size());
      mShownVertexList.resize(vertCount);
      for (size_t i = 0; i < vertCount; ++i)
      {
         mShownVertexList[i] = i;
      }
   }

   std::vector<int> rmverts(aVerts);
   std::sort(rmverts.begin(), rmverts.end());
   std::vector<int> remain;
   std::set_difference(mShownVertexList.begin(), mShownVertexList.end(), rmverts.begin(), rmverts.end(), std::back_insert_iterator<std::vector<int> >(remain));
   std::swap(mShownVertexList, remain);
   UpdateShownVertices();
}

void PolyMeshView::AddShownVertices(const std::vector<int>& aVerts)
{
   int vertCount(mVerts.size());
   if (mShownVertexList.size() == vertCount)
   {
      mShownVertexList.clear();
   }
   mShownVertexList.insert(mShownVertexList.end(), aVerts.begin(), aVerts.end());
   std::sort(mShownVertexList.begin(), mShownVertexList.end());
   mShownVertexList.erase(std::unique(mShownVertexList.begin(), mShownVertexList.end()), mShownVertexList.end());
   UpdateShownVertices();
}
void PolyMeshView::UpdateShownVertices()
{
//   
   std::set<int> verts;
   for (size_t i = 0; i < mShownVertexList.size(); ++i)
   {
      //VertexLinkMap::right_const_iterator iter = mVertexLinks.right.find(mShownVertexList[i]);
      //verts.insert(iter->second);
      verts.insert(mShownVertexList[i]);
   }
   
   mShownPolyList.clear();
   const PolyhedronList& pls = *polys;
   for (size_t i = 0; i < pls.size(); ++i)
   {
      const Polyhedron& p = pls[i];
      size_t j = 0;
      for (; j < size_t(p.arity); ++j)
      {
         if (verts.find(p.vertex[j]) == verts.end()) break;
      }
      if (j == p.arity)
      {
         mShownPolyList.push_back(i);
      }
   }
}
